package com.wondersgroup.restbase.controller;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.persistence.Entity;

import org.dozer.DozerBeanMapper;
import org.dozer.loader.api.BeanMappingBuilder;
import org.dozer.loader.api.TypeMappingOptions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

import com.wondersgroup.restbase.dto.RestResult;
import com.wondersgroup.restbase.service.BaseService;
import com.wondersgroup.restbase.service.DefaultService;

/**
 * 基于Map作为DTO的Web控制器抽象基类，利用开源的Dozer库做Map和业务实体的字段映射
 * @author wondersgroup
 *
 * @param <PK> 业务实体的主键类型泛型参数
 * @param <MODEL> 业务实体的类型泛型参数
 */
@Component
@SuppressWarnings("rawtypes")
public abstract class DozerBaseController<PK extends Serializable, MODEL> extends BaseController<Map, PK, MODEL> {

	protected DozerBeanMapper dozer;

	/**
	 * 初始化dozer对象
	 */
	public DozerBaseController() {

		dozer = new DozerBeanMapper();
		// TODO Auto-generated constructor stub
		configure(dozer);
	}

	/**
	 * 配置dozer对象
	 * @param dozer
	 */
	protected void configure(DozerBeanMapper dozer) {
		dozer.addMapping(getDefaultMappingBuilder());
	}

	/**
	 * 获取dozer的默认映射构造器
	 * @return 映射构造器 
	 */
	private BeanMappingBuilder getDefaultMappingBuilder() {
		return new BeanMappingBuilder() {
			@Override
			protected void configure() {
				configureMappingBuilder(this);

			}
		};
	}
	
	/**
	 * 将业务实体集合转换为map集合
	 */
	@Override
	protected List<Map> toDto(List<MODEL> models) {
		// TODO Auto-generated method stub
		if (models == null)
			return null;
		List<Map> dtoList = new ArrayList<Map>();
		for (MODEL model : models) {
			dtoList.add(toDto(model));
		}
		return dtoList;
	}

	/**
	 * 将业务实体转换为map
	 */
	@Override
	protected Map toDto(MODEL model) {
		// TODO Auto-generated method stub
		if(model == null)
			return null;
		return dozer.map(model, getDTOClass());
	}

	@Override
	protected Map mapToDto(Map<String, String> map) throws Exception {
		// TODO Auto-generated method stub
		return map;
	}

	/**
	 * 通过map构造业务实体
	 */
	@Override
	protected MODEL newModel(Map dto) {
		// TODO Auto-generated method stub
		if (dto == null)
			return null;
		return dozer.map(dto, getModelClass());
	}

	/**
	 * 通过map更新业务实体
	 */
	@Override
	protected MODEL update(Map dto, MODEL toUpdate) {
		// TODO Auto-generated method stub
		MODEL newModel = this.newModel(dto);
		dozer.map(newModel, toUpdate);
		return toUpdate;
	}

	/**
	 * 默认实现返回dto的类型为Map类型
	 */
	@Override
	protected Class<Map> getDTOClass() {
		// TODO Auto-generated method stub
		return Map.class;
	}

	/**
	 * 设置非空不映射的规则
	 * @param mappingBuilder 映射构造器
	 */
	protected void configureMappingBuilder(BeanMappingBuilder mappingBuilder) {
		mappingBuilder.mapping(getModelClass(), getModelClass(), TypeMappingOptions.mapNull(false),
				TypeMappingOptions.mapEmptyString(false));
	}

	/**
	 * 提供默认服务类
	 */
	@Override
	protected BaseService<MODEL> getService() {
		// TODO Auto-generated method stub
		return (BaseService<MODEL>) baseService;
	}

	/**
	 * 根据泛型参数获取实体类型
	 */
	@SuppressWarnings("unchecked")
	@Override
	protected Class<MODEL> getModelClass() {
		// TODO Auto-generated method stub
		Type genType = getClass().getGenericSuperclass();
		if (!(genType instanceof ParameterizedType)) {
			throw new RuntimeException("Can not determine the model class of controller");
		}
		Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
		for (Type type : params) {
			if (type instanceof Class && ((Class) type).getAnnotation(Entity.class) != null) {
				return (Class<MODEL>) type;
			}
		}
		throw new RuntimeException("Can not determine the model class of controller");
	}

	@Autowired
	@Qualifier("defaultServiceImpl")
	private DefaultService<MODEL> baseService;

	/**
	 * 通过实体类型初始化服务对象
	 */
	@PostConstruct
	public void init() {
		baseService.initModelClass(getModelClass());
	}
	
	/**
	 * 包裹任意数据为rest结果
	 * @param data 任意类型数据
	 * @return rest包裹对象
	 */
	protected <M> RestResult<M> getRestResult(M data) {
		return new RestResult<M>(data);
	}
	
	/**
	 * 根据模型类型获取对应的服务对象
	 * @param clazz 实体类型对象
	 * @return 服务对象
	 */
	protected <M> BaseService<M> getService(Class<M> clazz) {
		return getService().newService(clazz);
	}
}
